package net.csdn.business.webhook.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.domain.query.WebhookConfigQuery;
import net.csdn.business.common.domain.request.webhook.*;
import net.csdn.business.common.domain.vo.OrgInfoVo;
import net.csdn.business.common.domain.vo.UserVo;
import net.csdn.business.common.domain.vo.WebhookConfigVO;
import net.csdn.business.common.enums.ResultCodeEnum;
import net.csdn.business.common.enums.RoleEnum;
import net.csdn.business.common.exception.BusinessException;
import net.csdn.business.common.service.SuperServiceImpl;
import net.csdn.business.webhook.config.OpenApiConfig;
import net.csdn.business.webhook.config.WebHookConfigData;
import net.csdn.business.webhook.config.WebHookEventConfig;
import net.csdn.business.webhook.entity.WebhookConfig;
import net.csdn.business.webhook.entity.WebhookConfigEvent;
import net.csdn.business.webhook.mapper.WebhookConfigEventMapper;
import net.csdn.business.webhook.mapper.WebhookConfigMapper;
import net.csdn.business.webhook.service.IWebhookConfigService;
import net.csdn.business.webhook.service.IWebhookLogService;
import net.csdn.business.webhook.utils.EncryptUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @ClassName WebhookConfigServiceImpl
 * @Description webhook配置信息Service业务层处理
 * @Author zhangyl
 * @Date 2023/4/23 16:16
 */
@Slf4j
@Service
public class WebhookConfigServiceImpl extends SuperServiceImpl<WebhookConfigMapper, WebhookConfig> implements IWebhookConfigService
{

    @Value("${ali.mq.webhook.topic:WEB_HOOK_EVENT}")
    private String webhookTopic;

    @Autowired
    private OpenApiConfig openApiConfig;

    @Autowired
    private ProducerBean aliProducer;

    @Autowired
    private WebHookEventConfig webHookEventConfig;

    @Autowired
    private WebHookConfigData configData;

    @Autowired
    private IWebhookLogService webhookLogService;

    @Autowired
    private WebhookConfigEventMapper eventMapper;

    @Resource(name="serviceExecutor")
    private ThreadPoolTaskExecutor serviceExecutor;



    /**
     * 分页查询webhook配置信息列表
     *
     * @param query webhook配置信息
     * @return webhook配置信息
     */
    @Override
    public Page<WebhookConfigVO> findPageList(WebhookConfigQuery query) throws Exception{
        //获得Page对象
        Page<WebhookConfigVO> page = query.getPage();
        List<WebhookConfigVO> pageList=baseMapper.findPageList(page,query);
        //多线程执行1、对加密信息解密；2、查询对应的event;3、查询最近一条通知响应状态信息
        List<CompletableFuture<WebhookConfigVO>> futureList = new ArrayList<>();
        for (WebhookConfigVO webhookConfigVO:pageList){
            CompletableFuture<WebhookConfigVO> completableFuture=CompletableFuture.supplyAsync(()-> {
                String encryUrl = webhookConfigVO.getNotifyUrl();
                String encryptUrlIv = webhookConfigVO.getEncryptUrlIv();
                String decryptUrl = null;
                try {
                    decryptUrl = EncryptUtil.decryptString(encryUrl, encryptUrlIv, configData.getEncryptKey());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                webhookConfigVO.setNotifyUrl(decryptUrl);
                //查询对应webhook的event
                String webHookId = webhookConfigVO.getObjectId();
                List<String> events = eventMapper.selectWebhookConfigEventList(webHookId);
                webhookConfigVO.setEvents(events);
                //查询最近一次的通知的响应状态
                Map<String, Object> lastDelivery = webhookLogService.getLastDelivery(webHookId);
                webhookConfigVO.setLastDelivery(lastDelivery);
                return webhookConfigVO;
            },serviceExecutor);
            futureList.add(completableFuture);
        }
        //主线程等待所有任务的结束，这里按实际需求可以灵活运用。
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
        List<WebhookConfigVO> result=new ArrayList<>();
        futureList.stream().forEach(future->{
            try {
                result.add(future.get()); //这个future的get()这里是直接返回，因为上面的join已经保证任务已经结束；如果任务没结束，这个get()会等到结束才返回值
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
        pageList=result.stream().sorted(Comparator.comparing(WebhookConfigVO::getCreatedDate).reversed()).collect(Collectors.toList());
        page.setRecords(pageList);
        return page;
    }

    /**
     * 根据id查询webhook配置信息
     * 
     * @param id webhook配置信息主键
     * @return webhook配置信息
     */
    @Override
    public WebhookConfigVO selectWebhookConfigById(String id) throws Exception
    {
        WebhookConfigVO webhookConfigVO=baseMapper.selectWebhookConfigById(id);
        if(webhookConfigVO==null){
            throw new BusinessException(ResultCodeEnum.PARAMETER_ERROR_CUSTOM,"id");
        }
        String encryUrl=webhookConfigVO.getNotifyUrl();
        String encryptUrlIv=webhookConfigVO.getEncryptUrlIv();
        String decryptUrl=EncryptUtil.decryptString(encryUrl,encryptUrlIv,configData.getEncryptKey());
        webhookConfigVO.setNotifyUrl(decryptUrl);
        String encrySecret=webhookConfigVO.getSecret();
        String encryptSecretIv=webhookConfigVO.getEncryptSecretIv();
        if(StringUtils.isNotBlank(encrySecret)&&StringUtils.isNotBlank(encryptSecretIv)){
            String decryptSecret=EncryptUtil.decryptString(encrySecret,encryptSecretIv,configData.getEncryptKey());
            webhookConfigVO.setSecret(decryptSecret);
            webhookConfigVO.setIsSetSecret(true);
        }
        //查询关联的事件
        List<String> events=eventMapper.selectWebhookConfigEventList(id);
        webhookConfigVO.setEvents(events);
        return webhookConfigVO;
    }

    /**
     * 根据fromId查询webhook配置信息列表
     *
     * @param fromId webhook配置主体id
     * @return webhook配置信息集合
     */
    @Override
    public List<WebhookConfigVO> selectWebhookConfigList(String fromId)
    {
        List<WebhookConfigVO> result=baseMapper.selectWebhookConfigList(fromId);
        if(result!=null&&result.size()>0){
            //查询webhook对应的事件
            result.stream().parallel().forEach(webhookConfigVO->{
                //查询对应webhook的event
                String webHookId = webhookConfigVO.getObjectId();
                List<String> events = eventMapper.selectWebhookConfigEventList(webHookId);
                webhookConfigVO.setEvents(events);
            });
        }
        return result;
    }

    /**
     * 新增webhook配置信息
     *
     * @param param webhook配置信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String insertWebhookConfig(NewWebhookConfigDTO param) throws Exception
    {
        String userId=param.getUserId();
        String fromId=param.getFromId();
        String url=param.getNotifyUrl();
        Integer type=param.getType();
        switch (type){
            case 1:
                //验证当前用户是否是组织管理员
                String orgRole=null;
                if(orgRole==null||!RoleEnum.MAINTAINER.getCode().equals(orgRole)){
                    throw new BusinessException(ResultCodeEnum.UNAUTHORIZED);
                }
                break;
            case 2:
                //验证当前用户是否是项目管理员
                String projectRole=null;
                if(projectRole==null){
                    throw new BusinessException(ResultCodeEnum.UNAUTHORIZED);
                }
                break;
        }
        //对以下敏感关键字段进行aes加密
        String notifyUrl=param.getNotifyUrl();
        String secret=param.getSecret();
        //保存基础配置信息
        WebhookConfig webhookConfig=new WebhookConfig();
        BeanUtils.copyProperties(param,webhookConfig);
        if(StringUtils.isNotBlank(notifyUrl)){
            //生成iv偏移量
            String encryptUrlIv= RandomUtil.randomString(16);
            webhookConfig.setEncryptUrlIv(encryptUrlIv);
            //对其进行加密
            String encryptUrl=EncryptUtil.encryptString(notifyUrl,encryptUrlIv,configData.getEncryptKey());
            webhookConfig.setNotifyUrl(encryptUrl);
        }
        if(StringUtils.isNotBlank(secret)){
            //生成iv偏移量
            String encryptSecretIv= RandomUtil.randomString(16);
            webhookConfig.setEncryptSecretIv(encryptSecretIv);
            //对其进行加密
            String encryptSecret=EncryptUtil.encryptString(secret,encryptSecretIv,configData.getEncryptKey());
            webhookConfig.setSecret(encryptSecret);
        }
        webhookConfig.setCreatedDate(new Date());
        //设置webhookid
        String webHookId= IdUtil.fastSimpleUUID();
        webhookConfig.setCreatedBy(userId);
        webhookConfig.setObjectId(webHookId);
        int resConfig=baseMapper.insert(webhookConfig);
        List<String> events=param.getEvents();
        if(resConfig>0&&events!=null&&events.size()>0){
            List<WebhookConfigEvent> eventList=new ArrayList();
            events.stream().filter(e->!"".equals(e)).forEach(event->{
                WebhookConfigEvent eventEntity=new WebhookConfigEvent();
                eventEntity.setObjectId(IdUtil.fastSimpleUUID());
                eventEntity.setTriggerEvent(event);
                eventEntity.setCreatedBy(userId);
                eventEntity.setWebHookId(webHookId);
                eventList.add(eventEntity);
            });
            //批量保存事件
            if(eventList.size()>0){
                eventMapper.insertBatch(eventList);
            }
        }
        //通知云校创建对应的webhoook,
        WebHookNotifyAliDTO notifyAlParams=new WebHookNotifyAliDTO();
        notifyAlParams.setSource_id(param.getFromId());
        List<String> eventList=new ArrayList();
        for (String event:param.getEvents()){
            Set<String> projectEvent=webHookEventConfig.getProject();
            if(projectEvent.contains(event)){
                eventList.add(event);
            }
        }
        notifyAlParams.setEvents_type_list(eventList);
        return webHookId;
    }

    /**
     * 修改webhook配置信息
     *
     * @param param webhook配置信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer updateWebhookConfig(UpdateWebhookConfigDTO param) throws Exception
    {
        String id=param.getId();
        String userId=param.getUserId();
        //对以下敏感关键字段进行aes加密
        String notifyUrl=param.getNotifyUrl();
        String secret=param.getSecret();
        WebhookConfigVO webhookConfigVO=baseMapper.selectWebhookConfigById(id);
        if(webhookConfigVO==null){
            throw new BusinessException(ResultCodeEnum.PARAMETER_ERROR_CUSTOM,"id");
        }
        String fromId=webhookConfigVO.getFromId();
        Integer type=webhookConfigVO.getType();
        LambdaUpdateWrapper<WebhookConfig> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(WebhookConfig::getObjectId, id);
        WebhookConfig webhookConfig=new WebhookConfig();
        BeanUtils.copyProperties(param,webhookConfig);
        if(StringUtils.isNotBlank(notifyUrl)){
            //生成iv偏移量
            String encryptUrlIv= RandomUtil.randomString(16);

            webhookConfig.setEncryptUrlIv(encryptUrlIv);
            //对其进行加密
            String encryptUrl=EncryptUtil.encryptString(notifyUrl,encryptUrlIv,configData.getEncryptKey());
            webhookConfig.setNotifyUrl(encryptUrl);
        }
        if(StringUtils.isNotBlank(secret)){
            //生成iv偏移量
            String encryptSecretIv= RandomUtil.randomString(16);
            webhookConfig.setEncryptSecretIv(encryptSecretIv);
            //对其进行加密
            String encryptSecret=EncryptUtil.encryptString(secret,encryptSecretIv,configData.getEncryptKey());
            webhookConfig.setSecret(encryptSecret);
        }
        webhookConfig.setLastModifiedBy(userId);
        webhookConfig.setLastModifiedDate(new Date());
        int resConfig=baseMapper.update(webhookConfig,updateWrapper);
        List<String> events=param.getEvents();
        if(resConfig>0&&events!=null&&events.size()>0){
            //先删除原有关联event
            eventMapper.deleteWebhookEventByWebHookId(id);
            List<WebhookConfigEvent> eventList=new ArrayList();
            events.forEach(event->{
                WebhookConfigEvent eventEntity=new WebhookConfigEvent();
                eventEntity.setObjectId(IdUtil.fastSimpleUUID());
                eventEntity.setTriggerEvent(event);
                eventEntity.setCreatedBy(userId);
                eventEntity.setWebHookId(id);
                eventList.add(eventEntity);
            });
            //批量保存事件
            eventMapper.insertBatch(eventList);
        }
        //通知云校更新对应的webhoook,
        WebHookNotifyAliDTO notifyAlParams=new WebHookNotifyAliDTO();
        List<String> eventList=new ArrayList();
        for (String event:param.getEvents()){
            Set<String> projectEvent=webHookEventConfig.getProject();
            if(projectEvent.contains(event)){
                eventList.add(event);
            }
        }
        notifyAlParams.setEvents_type_list(eventList);
        return resConfig;
    }

    /**
     * 删除webhook配置信息信息
     * 
     * @param param 删除webhook配置请求信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer deleteWebhookConfigById(DeleteWebhookConfigDTO param)
    {
        String id=param.getId();
        String userId=param.getUserId();
        WebhookConfigVO webhookConfigVO=baseMapper.selectWebhookConfigById(id);
        if(webhookConfigVO==null){
          throw new BusinessException(ResultCodeEnum.PARAMETER_ERROR_CUSTOM,"id");
        }
        String fromId=webhookConfigVO.getFromId();
        Integer type=webhookConfigVO.getType();

        //删除webhook配置信息
        int res=baseMapper.deleteWebhookConfigById(id);
        if(res>0){
            //关联删除webhook配置的事件
            eventMapper.deleteWebhookEventByWebHookId(id);
        }
        return res;
    }

    @Override
    @Async("serviceExecutor")
    public void sendPing(String webHookId,NewWebhookConfigDTO params) throws Exception{
        //查询webhook详情
        WebhookConfigVO webhookConfigVO=this.selectWebhookConfigById(webHookId);
        //创建成功发送ping测试
        WebhookNotifyMessage pingParams=new WebhookNotifyMessage();
        pingParams.setEventType("ping");
        pingParams.setWebhookId(webHookId);
        pingParams.setTargetId(params.getFromId());
        /**
         * 触发事件的主体类型：1：organization;2：repository;  3: 应用
         */
        JSONObject payloadJson=new JSONObject();
        payloadJson.put("zen","Anything added dilutes everything else.");
        payloadJson.put("hook_id",webHookId);
        //添加触发webhook信息
        JSONObject webhook=new JSONObject();
        webhook.put("type","");
        webhook.put("id",webHookId);
        webhook.put("name","web");
        webhook.put("active",webhookConfigVO.getIsActive());
        webhook.put("events",webhookConfigVO.getEvents());
        JSONObject webHookConfig=new JSONObject();
        /** 请求体类型：1：application/json 2：application/x-www-form-urlencoded */
        Integer reqBodyType=webhookConfigVO.getReqBodyType();
        if(reqBodyType==1){
            webHookConfig.put("content_type","json");
        }else{
            webHookConfig.put("content_type","x-www-form-urlencoded");
        }
        webHookConfig.put("insecure_ssl",webhookConfigVO.getEnableSslVeri());
        webHookConfig.put("secret","********");
        webHookConfig.put("url",webhookConfigVO.getNotifyUrl());
        webhook.put("config",webHookConfig);
        webhook.put("updated_at",webhookConfigVO.getLastModifiedDate());
        webhook.put("created_at",webhookConfigVO.getCreatedDate());
        JSONObject lastResponse=new JSONObject();
        lastResponse.put("code","null");
        lastResponse.put("status","unused");
        lastResponse.put("message","null");
        webhook.put("last_response",lastResponse);
        String targetType=null;
        Integer type=params.getType();
        switch (type) {
            case 1:
                targetType="organization";
                webhook.put("type",targetType);
                payloadJson.put("hook",webhook);
                //组装组织相关信息
                JSONObject organization=getOrgInfo(params.getFromId());
                payloadJson.put("organization",organization);
                break;
            case 2:
                targetType="repository";
                webhook.put("type",targetType);
                payloadJson.put("hook",webhook);
                JSONObject repository=getProjectInfo(params.getFromId());
                payloadJson.put("repository",repository);
                break;
            case 3:
                targetType="integration";
                webhook.put("type",targetType);
                payloadJson.put("hook",webhook);
                JSONObject integration=new JSONObject();
                payloadJson.put("integration",integration);
                break;
        }
        pingParams.setTargetType(targetType);
        //添加触发ping发送者信息
        JSONObject sender=getUserInfo(params.getUserId());
        payloadJson.put("sender",sender);
        pingParams.setPayload(payloadJson.toJSONString());
        this.sendMsg(JSON.toJSONString(pingParams),"ping");
    }


    private void sendMsg(String content, String tag) {
        try {
            Message message = new Message(webhookTopic, tag, content.getBytes(StandardCharsets.UTF_8));
            SendResult result = aliProducer.send(message);
            log.info("==== tcp send MQ message success,topic={},tag={},content={},msgId={}", webhookTopic, tag, content, result.getMessageId());
        }catch (Exception e){
            log.info("==== tcp send MQ message faild,please check mq config!!!!");
        }
    }

    private JSONObject getProjectInfo(String projectId){
        //组装组织相关信息
        JSONObject repository=null;
        try{
            //根据项目id查询项目信息
            Long pid=null;
            if(StringUtils.isNotBlank(projectId)){
                pid=Long.parseLong(projectId);
            }
            //组装项目相关信息
            String pathWithNamespace=null;
            repository=new JSONObject();
            repository.put("id","");
            repository.put("name","");
            repository.put("full_name","");
            repository.put("private",true);
            JSONObject owner=getUserInfo("");
            repository.put("owner",owner);
            repository.put("html_url","");
            repository.put("description","");
            StringBuilder url=new StringBuilder(openApiConfig.getOpenApiDomain());
            url.append("/repos/").append(pathWithNamespace);
            repository.put("url",url.toString());
            String hooks_url=url+"/hooks";
            repository.put("hooks_url",hooks_url);
            String branches_url=url+"/branches{/branch}";
            repository.put("branches_url",branches_url);
            String tags_url=url+"/tags";
            repository.put("tags_url",tags_url);
            String git_tags_url=url+"/git/tags{/sha}";
            repository.put("git_tags_url",git_tags_url);
            String git_refs_url=url+"/git/refs{/sha}";
            repository.put("git_refs_url",git_refs_url);
            String trees_url=url+"/trees{/sha}";
            repository.put("trees_url",trees_url);
            String commits_url=url+"/commits{/sha}";
            repository.put("commits_url",commits_url);
            String comments_url=url+"/comments{/number}";
            repository.put("comments_url",comments_url);
            String issue_comment_url=url+"/issues/comments{/number}";
            repository.put("issue_comment_url",issue_comment_url);
            String merges_url=url+"/merges";
            repository.put("merges_url",merges_url);
            String issues_url=url+"/issues{/number}";
            repository.put("issues_url",issues_url);
            String pulls_url=url+"/pulls{/number}";
            repository.put("pulls_url",pulls_url);
            String milestones_url=url+"/milestones{/number}";
            repository.put("milestones_url",milestones_url);
            String labels_url=url+"/labels{/name}";
            repository.put("labels_url",labels_url);
            String releases_url=url+"/releases{/id}";
            repository.put("releases_url",releases_url);
            repository.put("created_at","");
            StringBuilder git_url=new StringBuilder("git://atomgit.com/");
            git_url.append(pathWithNamespace).append(".git");
            repository.put("git_url",git_url.toString());
            StringBuilder ssh_url=new StringBuilder("git@atomgit.com:");
            ssh_url.append(pathWithNamespace).append(".git");
            repository.put("ssh_url",ssh_url.toString());

        }catch (Exception e){
            e.printStackTrace();
        }
        return repository;
    }

    private JSONObject getOrgInfo(String orgId){
        //组装组织相关信息
        JSONObject organization=null;
        //根据组织id查询组织相关信息
        try{
            OrgInfoVo orgInfoVo=null;
            if(orgInfoVo!=null){
                organization=new JSONObject();
                String orgCode=orgInfoVo.getOrgCode();
                organization.put("login",orgCode);
                organization.put("id",orgId);
                StringBuilder orgUrl=new StringBuilder(openApiConfig.getOpenApiDomain());
                orgUrl.append("/orgs/").append(orgCode);
                organization.put("url",orgUrl.toString());
                String repos_url=orgUrl+"/repos";
                organization.put("repos_url",repos_url);
                String hooks_url=orgUrl+"/hooks";
                organization.put("hooks_url",hooks_url);
                String issues_url=orgUrl+"/issues";
                organization.put("issues_url",issues_url);
                String members_url=orgUrl+"/members/{/member}";
                organization.put("members_url",members_url);
                String logo = orgInfoVo.getLogo();
                if(StringUtils.isNotBlank(logo)){
                    StringBuilder avatarUrl=new StringBuilder(openApiConfig.getImageDomain());
                    avatarUrl.append(logo);
                    organization.put("avatar_url",avatarUrl.toString());
                }else{
                    organization.put("avatar_url","");
                }
                organization.put("description",orgInfoVo.getOrgDesc());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return organization;
    }


    private JSONObject getUserInfo(String userId){
        //根据id查询用户信息
        JSONObject user=null;
        try {
            UserVo userVo=null;
            if(userVo!=null){
                user=new JSONObject();
                user.put("id",userId);
                user.put("type","User");
                user.put("site_admin",false);
                user.put("login",userVo.getUsername());
                String photo=userVo.getPhoto();
                if(StringUtils.isNotBlank(photo)){
                    StringBuilder avatarUrl=new StringBuilder(openApiConfig.getImageDomain());
                    avatarUrl.append(photo);
                    user.put("avatar_url",avatarUrl.toString());
                }else{
                    user.put("avatar_url","");
                }
                StringBuilder url=new StringBuilder(openApiConfig.getOpenApiDomain());
                url.append("/").append(userVo.getUsername());
                user.put("url",url.toString());
                StringBuilder html_url=new StringBuilder(openApiConfig.getDomain());
                html_url.append("/").append(userVo.getUsername());
                user.put("html_url",html_url.toString());
                String followers_url=url+"/followers";
                user.put("followers_url", followers_url);
                String following_url=url+"/following{/other_user}";
                user.put("following_url",following_url);
                String organizations_url=url+"/orgs";
                user.put("organizations_url",organizations_url);
                String repos_url=url+"/repos";
                user.put("repos_url",repos_url);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return user;
    }

}
